{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "f511dc12",
   "metadata": {},
   "outputs": [],
   "source": [
    "#extract the regoin of all numbers\n",
    "import cv2 as cv\n",
    "import numpy as np\n",
    "from operator import itemgetter\n",
    "\n",
    "def findContoursBoundingRect(binaryImg, retrievalMode = cv.RETR_LIST):\n",
    "    approxMethod = cv.CHAIN_APPROX_NONE\n",
    "    contours, hierachy = cv.findContours(binaryImg, retrievalMode, approxMethod)\n",
    "    bRect = []\n",
    "    for cnt in contours:\n",
    "        x,y,w,h = cv.boundingRect(cnt)\n",
    "        bRect.append((x,y,w,h))\n",
    "    \n",
    "    return bRect\n",
    "\n",
    "def screenPossibleSingleNumRect(bRects):\n",
    "    bRect = []\n",
    "    for (x,y,w,h) in bRects:\n",
    "        prop = h*1./w\n",
    "        if prop > 1.5 - 0.3 and prop < 1.5 + 0.5:\n",
    "            bRect.append((x,y,w,h))\n",
    "    return bRect\n",
    "\n",
    "def screenPossibleContinousNumRect(bRects):\n",
    "    bRect = []\n",
    "    for (x,y,w,h) in bRects:\n",
    "        prop = w*1./h\n",
    "        if prop > 4 - 1 and prop < 4 + 1:\n",
    "            bRect.append((x,y,w,h))\n",
    "    return bRect\n",
    "\n",
    "def preRighttopXLessThanNextLefttopX(bRects):\n",
    "    for idx, (x,y,w,h) in enumerate(bRects):\n",
    "        if idx == len(bRects) - 1:\n",
    "            break\n",
    "        \n",
    "        cur_rect_righttop_x = x + w\n",
    "        next_rect_lefttop_x = bRects[idx+1][0]\n",
    "        if cur_rect_righttop_x >= next_rect_lefttop_x:\n",
    "            return False\n",
    "    \n",
    "    return True\n",
    "    \n",
    "def screenContinousNumRect(bRects): \n",
    "    realTarget = []\n",
    "    LIMIT_Y, TARGET_NUM = 10, 4\n",
    "    \n",
    "    for idx, (x,y,w,h) in enumerate(bRects):\n",
    "        length = len(bRects)\n",
    "        ref    = bRects[idx]\n",
    "        possibleTarget = [ref]\n",
    "\n",
    "        for remain_idx in range(idx+1, length):\n",
    "            comparedRect = bRects[remain_idx]\n",
    "            refY, comparedY = ref[1], comparedRect[1]\n",
    "            \n",
    "            if abs(refY - comparedY) <= LIMIT_Y:\n",
    "                possibleTarget.append(comparedRect)\n",
    "                \n",
    "                if len(possibleTarget) >= TARGET_NUM and \\\n",
    "                preRighttopXLessThanNextLefttopX(possibleTarget):\n",
    "                    realTarget = possibleTarget\n",
    "                    break\n",
    "            else:\n",
    "                continue\n",
    "    \n",
    "    return realTarget\n",
    "\n",
    "def putTextAboveNumber(img, text, textBottomLeftPt):\n",
    "    fontType = cv.FONT_HERSHEY_SIMPLEX\n",
    "    fontScale = 1\n",
    "    color   = (0,0,255)\n",
    "    thinknessPx = 1\n",
    "    lineType = cv.LINE_AA\n",
    "    cv.putText(img, text,textBottomLeftPt, \n",
    "               fontType,fontScale, color,thinknessPx,lineType)\n",
    "\n",
    "def seperateSingleNumberImg(binaryImg):\n",
    "    bRect = findContoursBoundingRect(binaryImg, retrievalMode = cv.RETR_EXTERNAL)\n",
    "    \n",
    "    bRect.sort(key=itemgetter(0)) #itemgetter(0) means x\n",
    "    \n",
    "    signleNumberImgs = []\n",
    "    for x,y,w,h in bRect:\n",
    "        numAlone = binaryImg[y:y+h, x:x+w].copy()\n",
    "        dst_w, dst_h = 60, 90\n",
    "        dst = cv.resize(numAlone,(dst_w, dst_h), interpolation = cv.INTER_CUBIC)\n",
    "        signleNumberImgs.append(dst)\n",
    "    \n",
    "    return signleNumberImgs\n",
    "\n",
    "def getSingleNumRefImgs():\n",
    "    ref = cv.imread(\"../data/creditCard/ocr_a_reference.png\")\n",
    "    refGray = cv.cvtColor(ref, cv.COLOR_BGR2GRAY)\n",
    "    ret, CCBinary = cv.threshold(refGray, 160, 255, 1)\n",
    "    return seperateSingleNumberImg(CCBinary)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "997e872b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "matchIdx: 5 matchValue: 0.6507156491279602\n",
      "matchIdx: 4 matchValue: 0.7436978816986084\n",
      "matchIdx: 1 matchValue: 0.6691306829452515\n",
      "matchIdx: 2 matchValue: 0.6920289993286133\n",
      "matchIdx: 7 matchValue: 0.7250968217849731\n",
      "matchIdx: 5 matchValue: 0.6554844975471497\n",
      "matchIdx: 1 matchValue: 0.6643710732460022\n",
      "matchIdx: 2 matchValue: 0.6838184595108032\n",
      "matchIdx: 3 matchValue: 0.6811836957931519\n",
      "matchIdx: 4 matchValue: 0.7917960286140442\n",
      "matchIdx: 5 matchValue: 0.6761834621429443\n",
      "matchIdx: 6 matchValue: 0.7396813035011292\n",
      "matchIdx: 7 matchValue: 0.745647132396698\n",
      "matchIdx: 8 matchValue: 0.665221631526947\n",
      "matchIdx: 9 matchValue: 0.6302592754364014\n",
      "matchIdx: 0 matchValue: 0.6762421131134033\n",
      "card number:  5412751234567890\n"
     ]
    }
   ],
   "source": [
    "# -------------------------\n",
    "# MAIN LOGIC FROM HERE\n",
    "# -------------------------\n",
    "creditCard = cv.imread(\"../data/creditCard/credit_card_03.png\")\n",
    "CCGray = cv.cvtColor(creditCard, cv.COLOR_BGR2GRAY)\n",
    "\n",
    "# ******\n",
    "# PREPROCESS: all possible single number rects\n",
    "# ******\n",
    "ret, CCBinary = cv.threshold(CCGray, 160, 255, 0)\n",
    "\n",
    "kernelRect = np.ones((3,3), np.uint8)\n",
    "CCBinaryPrepocessed = cv.morphologyEx(CCBinary, cv.MORPH_CLOSE, kernelRect)\n",
    "\n",
    "# ******\n",
    "# FIND: all possible single number rects\n",
    "# ******\n",
    "bRects = findContoursBoundingRect(CCBinaryPrepocessed)\n",
    "bRectPossibleSingleNum = screenPossibleSingleNumRect(bRects)\n",
    "\n",
    "# DRAW\n",
    "# draw0 = np.zeros(CCBinaryPrepocessed.shape, np.uint8)\n",
    "# drawForCCBinaryPrepocessed = cv.cvtColor(CCBinaryPrepocessed, cv.COLOR_GRAY2BGR)\n",
    "# for (x,y,w,h) in bRectPossibleSingleNum:    \n",
    "#     cv.rectangle(draw0, (x,y), (x+w,y+h), 255, 1)\n",
    "#     cv.rectangle(CCBinaryPrepocessed, (x,y), (x+w,y+h), (0,0,255), 1)\n",
    "\n",
    "# ******\n",
    "# PREPROCESS CONTINUE: all possible continous-number rects\n",
    "# ******\n",
    "CCBinaryPrepocessedCopy = CCBinaryPrepocessed.copy()\n",
    "for (x,y,w,h) in bRectPossibleSingleNum:    \n",
    "    cv.rectangle(CCBinaryPrepocessedCopy, (x,y), (x+w,y+h), 255, -1)\n",
    "\n",
    "kernelHon  = np.array([[0,0,0],[1,1,1],[0,0,0]], np.uint8)\n",
    "CCBinaryForCotinousNum = cv.dilate(CCBinaryPrepocessedCopy, kernelHon, iterations = 6)\n",
    "bRects = findContoursBoundingRect(CCBinaryForCotinousNum)\n",
    "bRectPossibleContinousNum = screenPossibleContinousNumRect(bRects)\n",
    "\n",
    "# ******\n",
    "# SELECT: 4 continous-number rects\n",
    "# ******\n",
    "bRectPossibleContinousNum.sort(key=itemgetter(0,1))\n",
    "FourContinousNumRect = screenContinousNumRect(bRectPossibleContinousNum)\n",
    "\n",
    "# DRAW\n",
    "draw2 = np.zeros(CCBinaryPrepocessed.shape, np.uint8)\n",
    "draw2ForCCBinary_dilate2 = cv.cvtColor(CCBinaryPrepocessed, cv.COLOR_GRAY2BGR)\n",
    "for  idx, (x,y,w,h) in enumerate(FourContinousNumRect):\n",
    "    cv.rectangle(draw2, (x,y), (x+w,y+h), 255, 1)\n",
    "    cv.rectangle(draw2ForCCBinary_dilate2, (x,y), (x+w,y+h), (0,0,255), 2)\n",
    "            \n",
    "# ******\n",
    "# EXTRACT: 4 continous-number ROI Img\n",
    "# ******\n",
    "ROIs = []\n",
    "for idx, (x,y,w,h) in enumerate(FourContinousNumRect):\n",
    "    ROIs.append(CCBinaryPrepocessed[y:y+h, x:x+w].copy())\n",
    "\n",
    "# ******\n",
    "# FIND: all single number ROI\n",
    "# ******\n",
    "#[another method: sort single num rect via X and \n",
    "#select single num rect is in the 4 multi-number rect]\n",
    "\n",
    "AllSingleNumImg = []\n",
    "for roi in ROIs:\n",
    "    AllSingleNumImg = AllSingleNumImg + seperateSingleNumberImg(roi)\n",
    "    \n",
    "# ******\n",
    "# RECOGNITION: recognize the number of image   \n",
    "# ******\n",
    "numberStr = [\"0\",\"1\",\"2\",\"3\",\"4\",\n",
    "             \"5\",\"6\",\"7\",\"8\",\"9\"]\n",
    "cardNumber = \"\"\n",
    "AllSingleNumRefImg = getSingleNumRefImgs()\n",
    "for signleNumImg in AllSingleNumImg:   \n",
    "    matchRes = []\n",
    "    for idx,singleNumRefImg in enumerate(AllSingleNumRefImg):\n",
    "        res = cv.matchTemplate(signleNumImg, singleNumRefImg, cv.TM_CCOEFF_NORMED)\n",
    "        matchRes.append(res)\n",
    "        \n",
    "    minV,maxV, minLoc,maxLoc = cv.minMaxLoc(np.array(matchRes))\n",
    "    print('matchIdx:', maxLoc[1], 'matchValue:', maxV)\n",
    "    \n",
    "    numIdx = maxLoc[1]\n",
    "    cardNumber += numberStr[numIdx]\n",
    "\n",
    "# ******\n",
    "# SHOW\n",
    "# ******\n",
    "print(\"card number: \", cardNumber)\n",
    "for idx, (x,y,w,h) in enumerate(FourContinousNumRect):\n",
    "    cardNumStartIdx = idx*4\n",
    "    putTextAboveNumber(creditCard, cardNumber[cardNumStartIdx:cardNumStartIdx+4], (x,y) )\n",
    "    \n",
    "cv.imshow(\"creditCard\", creditCard)\n",
    "cv.waitKey(0)\n",
    "cv.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "c81f087b",
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "invalid syntax (Temp/ipykernel_20056/1214197220.py, line 1)",
     "output_type": "error",
     "traceback": [
      "\u001b[1;36m  File \u001b[1;32m\"C:\\Users\\GRU-LE~1\\AppData\\Local\\Temp/ipykernel_20056/1214197220.py\"\u001b[1;36m, line \u001b[1;32m1\u001b[0m\n\u001b[1;33m    matchIdx: 4 matchValue: 0.9056315422058105\u001b[0m\n\u001b[1;37m                ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m invalid syntax\n"
     ]
    }
   ],
   "source": [
    "matchIdx: 4 matchValue: 0.9056315422058105\n",
    "matchIdx: 0 matchValue: 0.8855833411216736\n",
    "matchIdx: 2 matchValue: 0.9056539535522461\n",
    "matchIdx: 0 matchValue: 0.8855833411216736\n",
    "matchIdx: 3 matchValue: 0.9215268492698669\n",
    "matchIdx: 4 matchValue: 0.9056315422058105\n",
    "matchIdx: 0 matchValue: 0.8855833411216736\n",
    "matchIdx: 0 matchValue: 0.8855833411216736\n",
    "matchIdx: 0 matchValue: 0.8855833411216736\n",
    "matchIdx: 2 matchValue: 0.9056539535522461\n",
    "matchIdx: 3 matchValue: 0.9215268492698669\n",
    "matchIdx: 4 matchValue: 0.9056315422058105\n",
    "matchIdx: 5 matchValue: 0.8958724141120911\n",
    "matchIdx: 6 matchValue: 0.9112050533294678\n",
    "matchIdx: 7 matchValue: 0.9345963001251221\n",
    "matchIdx: 8 matchValue: 0.8973314762115479\n",
    "card number:  4020340002345678"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2fab4abe",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
